package pl.s7n.paint.edits;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.UndoableEditSupport;

import pl.s7n.paint.DrawableCanvas;
import pl.s7n.paint.commands.AbstractStrokeCmd;
import pl.s7n.paint.commands.ResizeCmd;
import pl.s7n.paint.utils.Pair;
import pl.s7n.paint.utils.Utils;

/**
 * Manages resizing @ transformations edits.<br>
 * On resize or transform edit, the current image is backed up to the resizes list, <br>
 * and so are the current drawing actions. These values are stored together @ the list of pairs:<br>
 * < image ; list of drawing actions >.<br>
 * <br>
 * This is just like saving a snapshot of canvas display @ all that has been done before, and remembering it<br>
 * back at the list.<br>
 * <br>
 * So from this moment on, what we have is a ready background image which is being rendered, and <br>
 * some DrawingCommands (if exist) as another layer. When Undo edit comes, the current background <br>
 * image is reverted to the previous one (currentImages list) or null (if currentImages is empty).
 * 
 * @author 7
 */
public class ResizeEditManager extends AbstractEditManager<ResizeCmd> {

  private DrawableCanvas                                     parent;

  private List<Pair<BufferedImage, List<AbstractStrokeCmd>>> resizes       = new ArrayList<Pair<BufferedImage, List<AbstractStrokeCmd>>>();
  private List<BufferedImage>                                currentImages = new ArrayList<BufferedImage>();

  public ResizeEditManager(UndoableEditSupport undoableEditSupport, DrawableCanvas parent) {
    super(undoableEditSupport);
    this.parent = parent;
  }

  @Override
  public void draw(Graphics2D g) {
    if (!currentImages.isEmpty()) {
      g.drawImage(currentImages.get(currentImages.size() - 1), 0, 0, null);
    }
  }

  @Override
  public void editPerformed(ResizeCmd action) {
    undoableEditSupport.postEdit(new ResizeEdit(action));

    doRedo(action);
  }

  private void doRedo(ResizeCmd action) {
    BufferedImage oldImage = action.getOldImage();
    List<AbstractStrokeCmd> drawingEdits = new ArrayList<AbstractStrokeCmd>(parent.getDrawingEditManager().actions);
    resizes.add(new Pair<BufferedImage, List<AbstractStrokeCmd>>(oldImage, drawingEdits));
    parent.getDrawingEditManager().reset();

    currentImages.add(Utils.copyImage(action.getNewImage()));
    parent.setImage(action.getNewImage(), false);
  }

  private void doUndo(ResizeCmd action) {
    if (!resizes.isEmpty()) {
      Pair<BufferedImage, List<AbstractStrokeCmd>> cur = resizes.remove(resizes.size() - 1);
      parent.setImage(cur.getFirst(), false);

      parent.getDrawingEditManager().reset();
      parent.getDrawingEditManager().actions.addAll(cur.getSecond());

      currentImages.remove(currentImages.size() - 1);
    }
    parent.redraw();
  }

  @Override
  public void reset() {
    resizes.clear();
    currentImages.clear();
  }

  @Override
  public boolean hasActions() {
    return !resizes.isEmpty();
  }

  private class ResizeEdit extends AbstractUndoableEdit {
    private static final long serialVersionUID = 335755372811578290L;

    ResizeCmd                 newAction;

    public ResizeEdit(ResizeCmd newAction) {
      this.newAction = newAction;
    }

    @Override
    public String getRedoPresentationName() {
      return "Redo " + newAction.toString();
    }

    @Override
    public String getUndoPresentationName() {
      return "Undo " + newAction.toString();
    }

    @Override
    public void undo() {
      doUndo(newAction);
      super.undo();
    }

    @Override
    public void redo() {
      doRedo(newAction);
      super.redo();
    }
  }
}
